import os
import multiprocessing
from multiprocessing import Process
import time


def demo_func(var,*agrs):
    print(f"demo_func,name:{__name__} pid:{os.getpid()} cu_process:{multiprocessing.current_process()} parent_process:{multiprocessing.parent_process()}")
    print(f"demo_func,var:{var} ,args:{agrs}")

class ReloadProcessClass(Process):
    def __init__(self,sleep_time:int=0):
        super().__init__()
        self.sleep_time=sleep_time

    def run(self):
        print(f"DemoClass,name:{__name__} pid:{os.getpid()}")
        print(f"DemoClass,sleep_time:{self.sleep_time}")
        print(f'DemoClass sleep start {time.time()}')
        if self.sleep_time>0:
            time.sleep(self.sleep_time)
        print(f'DemoClass sleep end {time.time()}')

def init_process_from_func():
    func_process=Process(target=demo_func,args=('init process from function','arg1','arg2',))
    func_process.start()
    func_process.join()

def init_process_from_class():
    reload_process=ReloadProcessClass(sleep_time=5)
    reload_process.start()
    reload_process.join()

# 进程间数据传递 Queue
from multiprocessing import Queue,JoinableQueue
def producer(queue,idx:int,time_interval:int,total_num=10,joinable_queue_flag=False):
    for i in range(total_num):
        n_task = f"p_{idx}_{i}"
        print(f"p_{idx}:<<{n_task}, size:{queue.qsize()}")
        if queue.full():
            print(f"p_{idx}: queue full")
        queue.put(n_task,block=True)
        time.sleep(time_interval)
    if joinable_queue_flag:
    # isinstance(queue,JoinableQueue)和isinstance(queue,Queue)写法会抛出奇怪的异常
    # if isinstance(queue,JoinableQueue):
        queue.join()
        print(f"p_{idx}:queue join")
    print(f"p_{idx} join")

def consumer(queue,idx:int,time_interval:int,joinable_queue_flag=False):
    while True:
        if queue.empty():
            print(f"c_{idx}:queue empty")
            break
        task = queue.get(block=True)
        if joinable_queue_flag:
        # isinstance(queue,JoinableQueue)和isinstance(queue,Queue)写法会抛出奇怪的异常
        # if isinstance(queue,JoinableQueue):
            queue.task_done()
        print(f"c_{idx}:>>{task}, size:{queue.qsize()}")
        time.sleep(time_interval)
        if queue.empty():
            print(f"c_{idx}:queue empty")
            break
    print(f"c_{idx} join")


def queue_demo():
    queue=Queue(maxsize=2)
    producer_process=Process(target=producer,args=(queue,0,0.5,10,))
    consumer_process_0=Process(target=consumer,args=(queue,0,2,))
    consumer_process_1=Process(target=consumer,args=(queue,1,2,))
    producer_process.daemon=True
    consumer_process_0.daemon=True
    consumer_process_1.daemon=True
    producer_process.start()
    consumer_process_0.start()
    consumer_process_1.start()
    producer_process.join()
    consumer_process_0.join()
    consumer_process_1.join()

def joinable_queue_demo():
    queue=JoinableQueue(maxsize=2)
    producer_process=Process(target=producer,args=(queue,1,0.5,10,True,))
    consumer_process_0=Process(target=consumer,args=(queue,1,2,True,))
    consumer_process_1=Process(target=consumer,args=(queue,1,2,True,))
    producer_process.daemon=True
    consumer_process_0.daemon=True
    consumer_process_1.daemon=True
    producer_process.start()
    consumer_process_0.start()
    consumer_process_1.start()
    producer_process.join()
    consumer_process_0.join()
    consumer_process_1.join()


# 进程间通信 Pipe，仅支持两个进程间的通讯
from multiprocessing import Pipe

def server(pipe,max_loops:int=10,interval:int=1):
    counter=0
    while True:
        if pipe.poll(3):
            recv_data=pipe.recv()
            print(f"server recv:{recv_data}")
            pipe.send(f"1")
            counter+=1
        if counter>=max_loops:
            break
        time.sleep(interval)
    print(f"server:exit")

def client(pipe,max_loops:int=10,interval:int=1):
    counter=0
    pipe.send(f"1")
    while True:
        if pipe.poll(3):
            recv_data=pipe.recv()
            print(f"client recv:{recv_data}")
            pipe.send(f"0")
            counter+=1
        if counter>=max_loops:
            break
        time.sleep(interval)
    print(f"client:exit")

def pipe_demo():
    # 仅能用于两个进程间双向传递信息
    pipe_server_connection,pipe_client_connection=Pipe()
    server_process=Process(target=server,args=(pipe_server_connection,10,1,))
    client_process=Process(target=client,args=(pipe_client_connection,10,1,))
    server_process.daemon=True
    client_process.daemon=True
    client_process.start()
    server_process.start()
    client_process.join()
    server_process.join()
    pipe_server_connection.close()
    pipe_client_connection.close()


# 进程间同步原语，一般会结合进程共享内存(multiprocessing.Array,multiprocessing.sharedctypes.Array)的方式使用
# multiprocessing.Array是multiprocessing.sharedctypes.Array的带锁实现，提供了简单的使用方法；
# multiprocessing.sharedctypes.Array是进程间共享内存的低层实现，灵活性较高，提供带锁和不带锁选择，支持自定义Struct
# Lock,RLock锁
from multiprocessing import Lock,RLock,Value,Array
def mother_board(lck,status_arr,status_len,id:int,update_interval:int=1):
    while True:
        time.sleep(update_interval)
        print(f"m_{id} arr:{status_arr[:]}")
        close_flag=True
        with lck:
            for i in range(status_len):
                status=status_arr[i]
                if status == 1:
                    close_flag=False
                    status_arr[i]=0
        if close_flag:
            print(f"m_{id}:close ,arr:{status_arr[:]}")
            break
    print(f'm_{id} processes exit')

def eqp_board(lck,status_arr,id:int,update_interval:int=1,max_loops:int=3):
    counter=0
    while True:
        print(f"e_{id} arr:{status_arr[:]}")
        lck.acquire(block=True)
        if status_arr[id] == 0:
            status_arr[id]=1
            counter+=1
        lck.release()
        time.sleep(update_interval)
        if counter>=max_loops:
            break
    print(f'e_{id} processes exit')

def sync_lck_demo():
    lck=Lock()
    status_arr=Array('i',[1,1,1])
    mother_board_process=Process(target=mother_board,args=(lck,status_arr,len(status_arr),0,3,))
    eqp_board_process_0=Process(target=eqp_board,args=(lck,status_arr,0,1,))
    eqp_board_process_1=Process(target=eqp_board,args=(lck,status_arr,1,1,))
    eqp_board_process_2=Process(target=eqp_board,args=(lck,status_arr,2,1,))
    mother_board_process.daemon=True
    eqp_board_process_0.daemon=True
    eqp_board_process_1.daemon=True
    eqp_board_process_2.daemon=True
    mother_board_process.start()
    eqp_board_process_0.start()
    eqp_board_process_1.start()
    eqp_board_process_2.start()
    mother_board_process.join()
    eqp_board_process_0.join()
    eqp_board_process_1.join()
    eqp_board_process_2.join()

# TODO: Condition,Event,Semaphore,Barrier,Timer示例

# Manager: 提供丰富的跨进程数据传递方法，甚至能够跨设备间进程通信
# TODO:标注前端与训练后端示例

if __name__ == "__main__":
    sync_lck_demo()